import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import java.awt.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;

public class GitUpdaterGUI {
    private static String rootDir;
    private static JTextArea textArea;
    private static JPanel panel;
    private static ArrayList<File> modulesToUpdate = new ArrayList<>();
    private static JComboBox<String> modulesComboBox;
    private static JScrollPane treeView = null;
    private static JFrame frame;

    public static void main(String[] args) {
        getRootPath();
        SwingUtilities.invokeLater(GitUpdaterGUI::createAndShowGUI);
    }

    private static void getRootPath() {
        // try to automatically get parent directory of the current project directory
        String currentPath = Paths.get("").toAbsolutePath().toString();
        Path startingPath = Paths.get(currentPath);
        String driveLetter = startingPath.getRoot().toString();
        boolean pathFound = false;

        for (int i = startingPath.getNameCount(); i >= 0; i--) {
            Path parentPath = Paths.get(driveLetter, startingPath.subpath(0, i).toString());
            // Path parentPath = Paths.get(startingPath.subpath(0, i).toString());
            if(parentPath.toString().toLowerCase().contains("root") ){
                int dialogResult = JOptionPane.showConfirmDialog(null, "是否使用此路径作为根目录？\n" + parentPath, "根目录选择", JOptionPane.YES_NO_CANCEL_OPTION);
                if (dialogResult == JOptionPane.YES_OPTION) {
                    rootDir = parentPath.toString();
                    pathFound = true;
                    break;
                } else if (dialogResult == JOptionPane.CANCEL_OPTION || dialogResult == JOptionPane.CLOSED_OPTION) {
                    System.exit(0);
                }
            }
        }

        if (!pathFound) { // user input root directory
            rootDir = JOptionPane.showInputDialog("未找到含有“root”的路径，请手动输入根目录地址：");
            if (rootDir == null) {
                System.exit(0);
            }
        }
    }

    private static void createAndShowGUI() {
        frame = new JFrame("Git Updater");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));

        textArea = new JTextArea(18, 60);
        textArea.setEditable(false);
        JScrollPane scrollPane = new JScrollPane(textArea);
        panel.add(scrollPane);

        JFrame finalFrame = frame;
        addGUIWorker(finalFrame);

        frame.getContentPane().add(panel, BorderLayout.CENTER);

        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private static void addGUIWorker(JFrame finalFrame) {
        SwingWorker<Void, Void> worker = new SwingWorker<>() {
            @Override
            protected Void doInBackground() {
                ExecutorService executor = Executors.newFixedThreadPool(5);  // 使用固定线程池并发执行任务
                Map<Future<Boolean>, CheckUpdatesTask> tasks = new HashMap<>();  // 用一个映射来存储任务

                File folder = new File(rootDir);
                File[] listOfFiles = folder.listFiles();
                if(listOfFiles != null){
                    for (File file : listOfFiles) {
                        if (file.isDirectory()) {
                            CheckUpdatesTask task = new CheckUpdatesTask(file);  // Create task
                            tasks.put(executor.submit(task), task);  // 将每个Future对象与其对应的CheckUpdatesTask关联起来
                        }
                    }
                }

                tasks.forEach((future, task) -> {
                    try {
                        if (future.get()) {  // 使用Future对象获取检查结果
                            modulesToUpdate.add(task.getFile());  // 如果需要更新，将文件添加到要更新的模块列表中
                        }
                    } catch (InterruptedException | ExecutionException e) {
                        e.printStackTrace();
                    }
                });

                executor.shutdown();  // 关闭ExecutorService
                return null;
            }

            @Override
            protected void done() {
                updateInfoShow();
                updateAllModule();
                updateSingleModule();
                createAndShowTree();
            }

            private void updateInfoShow() {
                appendToTextArea("=========================");
                appendToTextArea("需要更新的模块：");
                appendToTextArea("=========================");
                for (File file : modulesToUpdate) {
                    appendToTextArea(file.getName());
                }
                appendToTextArea("=========================");
            }

            private void updateSingleModule() {
                ArrayList<String> moduleNames = new ArrayList<>();
                for (File file : modulesToUpdate) {
                    moduleNames.add(file.getName());
                }

                modulesComboBox = new JComboBox<>(moduleNames.toArray(new String[0]));

                JButton button = new JButton("Update Selected");
                button.addActionListener(ActionEvent -> {
                    String selectedModuleName = (String) modulesComboBox.getSelectedItem();
                    for (File file : modulesToUpdate) {
                        if (file.getName().equals(selectedModuleName)) {
                            runGitUpdate(file);
                            break;
                        }
                    }
                });

                // Add the combo box and the button to the panel
                panel.add(modulesComboBox);
                panel.add(button);

                finalFrame.pack();
            }

            private void updateAllModule() {
                JButton updateAllButton = new JButton("Update All");
                updateAllButton.addActionListener(ActionEvent -> {
                    ExecutorService executor = Executors.newFixedThreadPool(5); // 使用线程池并发执行任务

                    for (File file : modulesToUpdate) {
                        executor.submit(() -> runGitUpdate(file)); // 在一个新线程中运行更新任务
                    }

                    executor.shutdown(); // 关闭ExecutorService

                    try {
                        executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    if (executor.isTerminated()) {
                        updateSuccessView();
                    }
                });
                panel.add(updateAllButton); // 将按钮添加到面板
            }
        };

        worker.execute();
    }

    public static boolean checkForUpdates(File module) {
        String checkHeadCommand = "git --git-dir=" + module.getPath() + "/.git --work-tree=" + module.getPath() + " symbolic-ref --short -q HEAD";
        String branch = executeCommand(checkHeadCommand);
        if (branch.isEmpty()) {
            appendToTextArea(module.getName() + " 的 HEAD 不指向任何分支，跳过检查。");
            return false;
        }

        String command = "git --git-dir=" + module.getPath() + "/.git --work-tree=" + module.getPath() + " remote update";
        try {
            Process process = Runtime.getRuntime().exec(command);
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            BufferedReader stderr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            String line;
            String local = executeCommand("git --git-dir=" + module.getPath() + "/.git --work-tree=" + module.getPath() + " rev-parse @");
            String remote = executeCommand("git --git-dir=" + module.getPath() + "/.git --work-tree=" + module.getPath() + " rev-parse @{u}");
            if (!local.equals(remote)) {
                appendToTextArea(module.getName() + " 需要更新");
                return true;
            } else {
                appendToTextArea(module.getName() + " 已是最新");
            }
            reader.close();
            stderr.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    private static void runGitUpdate(File module) {
        String command = "git --git-dir=" + module.getPath() + "/.git --work-tree=" + module.getPath() + " pull";
        try {
            Process process = Runtime.getRuntime().exec(command);
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                // Update the textArea in the Event Dispatch Thread
                String finalLine = line;
                appendToTextArea(finalLine);
            }
            // Wait for the process to finish and check the exit value
            int exitVal = process.waitFor();
            if (exitVal == 0) {
                appendToTextArea(module.getName() + " 模块更新成功");
                modulesToUpdate.remove(module);  // Remove the updated module
                updateSuccessView();
            } else {
                appendToTextArea(module.getName() + " 模块更新失败");
            }
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void updateSuccessView() {
        createAndShowTree();
        updateComboBox();
    }

    private static String executeCommand(String command) {
        StringBuilder output = new StringBuilder();
        try {
            Process process = Runtime.getRuntime().exec(command);
            process.waitFor();
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                output.append(line);
            }
            reader.close();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        return output.toString();
    }

    private static void appendToTextArea(String message) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        String timestamp = dateFormat.format(new Date());

        SwingUtilities.invokeLater(() -> {
            textArea.append(timestamp + " " + message + "\n");
            textArea.setCaretPosition(textArea.getDocument().getLength());
        });
    }

    // tree
    private static DefaultMutableTreeNode createNodes() {
        DefaultMutableTreeNode root = new DefaultMutableTreeNode("Module Updates");
        for (File file : modulesToUpdate) {
            root.add(new DefaultMutableTreeNode(file.getName()));
        }
        return root;
    }

    private static void createAndShowTree() {
        DefaultMutableTreeNode root = createNodes();
        JTree tree = new JTree(root);

        // Remove any existing scroll pane (and the tree inside it)
        if (treeView != null) {
            frame.getContentPane().remove(treeView);
        }

        treeView = new JScrollPane(tree);
        frame.getContentPane().add(treeView, BorderLayout.EAST);
        frame.validate();
    }

    private static void updateComboBox() {
        ArrayList<String> moduleNames = new ArrayList<>();
        for (File file : modulesToUpdate) {
            moduleNames.add(file.getName());
        }

        // Replace the model with an updated model
        DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>(moduleNames.toArray(new String[0]));
        modulesComboBox.setModel(model);
    }
}

class CheckUpdatesTask implements Callable<Boolean>  // 定义新的Callable任务来检查更新
{
    private final File file;

    public CheckUpdatesTask(File file) {
        this.file = file;
    }

    @Override
    public Boolean call() {
        return GitUpdaterGUI.checkForUpdates(file);  // 在call方法中执行检查更新的任务
    }

    public File getFile() {
        return file;
    }
}